package com.fileupload.fileupload.service.impl;

import com.fileupload.fileupload.domain.entity.ChunkFile;
import com.fileupload.fileupload.domain.vo.R;
import com.fileupload.fileupload.service.FileUploadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;

import static java.util.concurrent.TimeUnit.DAYS;

@Slf4j
@Service
public class ExcelFileUploadServiceImpl implements FileUploadService {
    private static final String TEMP_DIR = "uploads/temp/";
    private static final String MERGED_DIR = "uploads/merged/";

    @Resource(name = "redisTemplate")
    private RedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 保存分片文件
     * @param request
     * @throws IOException
     */
    @Override
    public R saveChunk(ChunkFile request) throws IOException {
        int chunkIndex = request.getChunkIndex();
        int totalChunks = request.getTotalChunks();
        String fileId = request.getFileId();
        MultipartFile file = request.getFile();
        String extension = request.getExtension();

        // 先判断这个文件是否是上传完毕了
        File mergedFile = new File(MERGED_DIR + fileId + "." + extension);
        if (mergedFile.exists()) {
            log.info("文件已存在，直接返回成功");
            return R.error("文件已存在");
        }

        // 判断当前传来的索引和redis中的索引是否一致
        Long longSize = redisTemplate.opsForList().size(fileId + "_chunks");
        if (longSize != chunkIndex){
            log.info("分片索引不一致，返回失败");
            return R.error(longSize,"分片索引不一致");
        }

        File tempDir = new File(TEMP_DIR);
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }

        File tempFile = new File(TEMP_DIR + fileId + "_" + chunkIndex);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(tempFile);
            fos.write(file.getBytes());
            // 将分片索引保存到redis中，并设置过期时间
            redisTemplate.opsForList().rightPush(fileId + "_chunks", chunkIndex);
            redisTemplate.expire(fileId + "_chunks", 1, DAYS);
        } finally {
            fos.close();
        }

        if (chunkIndex == totalChunks - 1) {
            mergeChunks(fileId, totalChunks ,extension);
        }

        log.info("分片文件保存成功，文件ID: {}, 分片索引: {}", fileId, chunkIndex);
        return R.success("分片文件保存成功");
    }

    /**
     * 合并分片文件
     * @param fileId
     * @param totalChunks
     * @throws IOException
     */
    private void mergeChunks(String fileId, int totalChunks ,String extension) throws IOException {
        File mergedDir = new File(MERGED_DIR);
        if (!mergedDir.exists()) {
            mergedDir.mkdirs();
        }
        File mergedFile = new File(MERGED_DIR + fileId + "." + extension);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(mergedFile);
            for (int i = 0; i < totalChunks; i++) {
                File chunkFile = new File(TEMP_DIR + fileId + "_" + i);
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(chunkFile); // 创建文件输入流
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = fis.read(buffer)) > 0) {
                        fos.write(buffer, 0, bytesRead);
                    }
                } finally {
                    // 关闭文件输入流
                    if (fis != null) {
                        fis.close();
                    }
                }
                chunkFile.delete();
            }
            // excel文件上传完成开始将excel文件读到数据库中
            String path = mergedFile.getPath();
            rabbitTemplate.convertAndSend("file-upload-exchange", "excel.queue", path);
            log.info("分片文件合并成功，文件ID: {}", fileId);
        } finally {
            fos.close();
        }
    }

}
